package lib;

import static util.Constant.FILE_TAGS;
import static util.Constant.PATH_DATA;
import static util.Constant.STR_NAME_NULL;
import static util.Tool.getMaxID;
import io.InOutput;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;

import util.Comparator;
import base.Event;
import base.Tag;

/**
 * The TagManager {@code class}. Manage the tags
 * 
 * @author Peter NGO, Guillaume POUSSEL and Mathieu RUL
 * @version 1.0
 */
public class TagManager {

    /**
     * The {@code TagManager} instance.
     */
    private static TagManager managerInstance;

    /**
     * Get an instance of {@code TagManager}.
     * 
     * @return an instance of {@code TagManager}.
     */
    public static TagManager getInstance() {
        if (managerInstance == null) {
            managerInstance = new TagManager();
        }
        return managerInstance;
    }

    /**
     * The {@code Hashtable} containing the Tag object.
     */
    private Hashtable<Integer, Object> tagTable;

    /**
     * The {@code String} containing the path of Tag data.
     */
    private String path = PATH_DATA + FILE_TAGS;

    /**
     * Constructor of the {@code class}. Create the user manager.
     */
    private TagManager() {
        initialize();
        try {
            load();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Create a new Tag
     * 
     * @param name the tag name

     * @return The created tag
     * 
     * @throws IllegalArgumentException
     */
    public Tag create(String name) {
        if (name == null || name.trim().isEmpty())
            throw new IllegalArgumentException(STR_NAME_NULL);
        Integer ID = getMaxID(tagTable) + 1;
        Tag tag = new Tag(ID, name);
        tagTable.put(ID, tag);
        save();
        return tag;
    }
    
    /**
     * Get a tag with its ID
     * 
     * @param ID the tag ID
     */
    public Tag get(Integer ID) {
        return (tagTable.get(ID) == null ? null : (Tag) tagTable.get(ID));
    }
    
    /**
     * Check if a tag with a given name exists
     * 
     * @return <code>null</code> or a Tag
     */
    public Tag get(String name) {
        for (Object tagObj : tagTable.values()) {
            Tag tag = (Tag) tagObj;
            if (tag.getName().equals(name)) return tag;
        }
        return null;
    }
    
    public Set<Tag> getAll() {
        Set<Tag> tags = new HashSet<Tag>();
        for (Object obj : tagTable.values()) {
            tags.add((Tag) obj);
        }
        return tags;
    }

    /**
     * Initialize the instance.
     */
    public void initialize() {
        tagTable = new Hashtable<Integer, Object>();
    }

    /**
     * Load the database in user table.
     * 
     * @throws Exception
     */
    private void load() throws Exception {
        tagTable = InOutput.read(path);
    }

    /**
     * Remove a tag
     * 
     * @param ID the tag ID
     */
    public Tag remove(Integer ID) {
        if (!tagTable.containsKey(ID)) return null;
        for (Event e : EventManager.getInstance().getAll()) {
            e.remove(ID);
        }
        Tag tag = (Tag) tagTable.remove(ID);
        save();
        return tag;
    }

    /**
     * Save the tag table in database.
     */
    private  void save() {
        InOutput.write(tagTable, path);
    }
    
    /**
     * Search a tag with its name
     * 
     * @param name the tag Name
     * @throws Exception 
     */
    public Tag[] searchByName(String name) throws Exception {
        for (Object tagObj : tagTable.values()) {
            Tag tag = (Tag) tagObj;
            if (tag.getName().equals(name)) return new Tag[] { tag };
        }
        
        Set<Tag> tags = new HashSet<Tag>();
        for (Object tagObj : tagTable.values()) {
            Tag tag = (Tag) tagObj;
            if (Comparator.compare(tag.getName(), name)) tags.add(tag);
        }
        
        return tags.toArray(new Tag[tags.size()]);
    }
    
    /**
     * Set the path.
     * 
     * @param path the path to set.
     */
    public void setPath(String path) {
        this.path = path;
    }
}
